package com.example.demo.service;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.logging.Logger;

@Service
public class DocumentGenerationService {

    private static final Logger logger = Logger.getLogger(DocumentGenerationService.class.getName());


    private ChatClient chatClient;
    private final Map<String, String> documentCache = new LinkedHashMap<>(100, 0.75f, true); // LRU 缓存
    private final String baseOutputPath;

    public DocumentGenerationService(ChatClient chatClient,
                                     @Value("${document.output-path:generated-api-docs}") String outputDir) {
        this.chatClient = chatClient;
        this.baseOutputPath = outputDir;
        createOutputDirectory();
    }

    private void createOutputDirectory() {
        try {
            Files.createDirectories(Paths.get(baseOutputPath));
        } catch (IOException e) {
            throw new RuntimeException("无法创建输出目录：" + baseOutputPath, e);
        }
    }

    public CompletableFuture<String> generateDocumentAsync(String folderPath, String template) {
        return CompletableFuture.supplyAsync(() -> {
            long startTime = System.currentTimeMillis();

            try {
                File folder = new File(folderPath);
                if (!folder.exists() || !folder.isDirectory()) {
                    throw new IllegalArgumentException("无效的目录路径: " + folderPath);
                }

                List<String> controllerContents = readControllerFiles(folder);
                String prompt = buildPrompt(template, controllerContents);
                String cachedDoc = getFromCache(prompt);

                if (cachedDoc != null) {
                    logger.info("使用缓存文档");
                    saveToFile(cachedDoc, getVersionedFilePath("openapi.json"));
                    logPerformance(startTime, "缓存命中");
                    return "使用缓存文档";
                }

                // Step 1: 生成原始文档
                String rawOpenApiJson = chatClient.prompt().user(prompt).call().content();
                saveToFile(rawOpenApiJson, getVersionedFilePath("openapi.json"));

                // Step 2: 优化文档内容
                String refinedPrompt = refinePrompt(rawOpenApiJson);
                String refinedOpenApiJson = chatClient.prompt().user(refinedPrompt).call().content();

                // Step 3: 保存并缓存
                String outputFilePath = getVersionedFilePath("refined-openapi.json");
                saveToFile(refinedOpenApiJson, outputFilePath);
                updateCache(prompt, refinedOpenApiJson);

                logPerformance(startTime, "新生成文档");

                return "文档已生成并保存至 " + outputFilePath;

            } catch (Exception e) {
                logger.severe("生成文档失败: " + e.getMessage());
                return "生成文档失败：" + e.getMessage();
            }
        });
    }

    private String buildPrompt(String template, List<String> contents) {
        StringBuilder sb = new StringBuilder();

        sb.append("你是一个资深 Java 开发工程师，请根据以下 Spring Boot Controller 源码生成 API 接口文档。\n");
        sb.append("要求如下：\n");

        if ("swagger".equalsIgnoreCase(template)) {
            sb.append("- 输出格式必须为标准的 OpenAPI 3.0 JSON\n");
            sb.append("- 包含完整接口名称、URL、方法、参数说明、返回值格式等信息\n");
            sb.append("- 对每个字段添加详尽的中文注释（description）\n");
            sb.append("- 必须严格遵循 OpenAPI 3.0 官方规范，不包含任何额外说明文字\n");
            sb.append("- 示例格式如下：\n");
            sb.append("  {\n");
            sb.append("    \"openapi\": \"3.0.0\",\n");
            sb.append("    \"info\": { \"title\": \"Sample API\", \"version\": \"1.0.0\" },\n");
            sb.append("    \"paths\": {\n");
            sb.append("      \"/example\": { \"get\": { \"summary\": \"示例接口\", \"responses\": {} } }\n");
            sb.append("    }\n");
            sb.append("  }\n");
            sb.append("- 请仅基于上述源码内容生成文档，不要自行扩展或虚构内容\n");
        }

        sb.append("- 尽量提取详细的注释信息（如 @RequestMapping, @GetMapping 等）\n");
        sb.append("- 如果有多个接口，按模块分类输出\n");
        sb.append("\n以下是 Controller 源码中的关键信息：\n\n");

        for (String content : contents) {
            sb.append(extractKeyMethodInfo(content)).append("\n\n");
        }

        return sb.toString();
    }


    private String refinePrompt(String jsonContent) {
        return "请保持以下 JSON 格式不变，仅对内容进行语义优化和注释补充，使其更符合 OpenAPI 3 规范，不要添加任何额外说明或解释。\n\n" +
                jsonContent;
    }


    private String extractKeyMethodInfo(String content) {
        StringBuilder sb = new StringBuilder();
        String[] lines = content.split("\n");

        boolean inMethod = false;
        for (String line : lines) {
            if (line.contains("@RequestMapping") ||
                    line.contains("@GetMapping") ||
                    line.contains("@PostMapping") ||
                    line.contains("@PutMapping") ||
                    line.contains("@DeleteMapping")) {

                inMethod = true;
                sb.append(line.trim()).append("\n");
            }
            else if (inMethod && line.trim().startsWith("public")) {
                sb.append(line.trim()).append("\n");
                inMethod = false;
            }
            else if (inMethod && line.trim().startsWith("*")) {
                sb.append(line.trim()).append("\n");
            }
        }

        return sb.toString();
    }


    private boolean isValidJson(String jsonContent) {
        try {
            new ObjectMapper().readTree(jsonContent);
            return true;
        } catch (Exception e) {
            logger.severe("JSON 格式校验失败: " + e.getMessage());
            return false;
        }
    }





    private List<String> readControllerFiles(File folder) throws IOException {
        List<String> contents = new ArrayList<>();
        traverseDirectory(folder, contents);
        return contents;
    }

    private void traverseDirectory(File folder, List<String> contents) throws IOException {
        for (File file : folder.listFiles()) {
            if (file.isDirectory()) {
                traverseDirectory(file, contents);
            } else if (file.getName().endsWith(".java")) {
                contents.add(Files.readString(file.toPath()));
            }
        }
    }

    private void saveToFile(String content, String filePath) throws IOException {
        Path path = Paths.get(filePath);
        Files.createDirectories(path.getParent());
        Files.writeString(path, content);
    }

    private String getVersionedFilePath(String filename) {
        String version = LocalDateTime.now().format(java.time.format.DateTimeFormatter.ofPattern("yyyyMMdd-HHmmss"));
        return baseOutputPath + File.separator + version + "-" + filename;
    }

    private String getFromCache(String prompt) {
        return documentCache.get(prompt);
    }

    private void updateCache(String prompt, String content) {
        documentCache.put(prompt, content);
    }

    private void logPerformance(long start, String action) {
        long end = System.currentTimeMillis();
        logger.info(String.format("%s，总耗时：%dms", action, end - start));
    }
}
